package com.subsecret.tuples;

import java.io.Serializable;

/**
 * 
 * @author Steffen Mikkelsen (www.subsecret.dk)
 * 
 */
public class Tuple {
    
    public static <S, T> Tuple2<S, T> of(S first, T second) {
        return new Tuple2<S, T>(first, second);
    }
    
    public static <S, T, U> Tuple3<S, T, U> of(S first, T second, U third) {
        return new Tuple3<S, T, U>(first, second, third);
    }
    
    public static <S, T, U, W> Tuple4<S, T, U, W> of(S first, T second, U third, W forth) {
        return new Tuple4<S, T, U, W>(first, second, third, forth);
    }
    
    public static <S, T, U, W, X> Tuple5<S, T, U, W, X> of(S first, T second, U third, W forth, X fifth) {
        return new Tuple5<S, T, U, W, X>(first, second, third, forth, fifth);
    }
    
    public static class Tuple2<S, T> implements Serializable {
        private static final long serialVersionUID = 1L;
        private S first;
        private T second;
        
        public Tuple2(S first, T second) {
            this.first = first;
            this.second = second;
        }
        
        public S getFirst() {
            return first;
        }
        
        public T getSecond() {
            return second;
        }
        
        @Override
        public int hashCode() {
            int hc = 0;
            if (first != null) {
                hc += 7 * first.hashCode();
            }
            if (second != null) {
                hc += 13 * second.hashCode();
            }
            
            return hc;
        }
        
        @Override
        public boolean equals(Object other) {
            if (!(other instanceof Tuple2<?, ?>)) {
                return false;
            }
            
            final Tuple2<?, ?> otherTuple = (Tuple2<?, ?>) other;
            
            if (this.getFirst() != null && !this.getFirst().equals(otherTuple.getFirst())) {
                return false;
            }
            if (this.getFirst() == null && otherTuple.getFirst() != null) {
                return false;
            }
            
            if (this.getSecond() != null && !this.getSecond().equals(otherTuple.getSecond())) {
                return false;
            }
            if (this.getSecond() == null && otherTuple.getSecond() != null) {
                return false;
            }
            
            return true;
        }
        
    }
    
    public static class Tuple3<S, T, U> implements Serializable {
        private static final long serialVersionUID = 1L;
        private S first;
        private T second;
        private U third;
        
        public Tuple3(S first, T second, U third) {
            this.first = first;
            this.second = second;
            this.third = third;
        }
        
        public S getFirst() {
            return first;
        }
        
        public T getSecond() {
            return second;
        }
        
        public U getThird() {
            return third;
        }
        
        @Override
        public int hashCode() {
            int hc = 0;
            if (first != null) {
                hc += 7 * first.hashCode();
            }
            if (second != null) {
                hc += 13 * second.hashCode();
            }
            
            if (third != null) {
                hc += 17 * third.hashCode();
            }
            
            return hc;
        }
        
        @Override
        public boolean equals(Object other) {
            if (!(other instanceof Tuple3<?, ?, ?>)) {
                return false;
            }
            
            final Tuple3<?, ?, ?> otherTuple = (Tuple3<?, ?, ?>) other;
            
            if (this.getFirst() != null && !this.getFirst().equals(otherTuple.getFirst())) {
                return false;
            }
            if (this.getFirst() == null && otherTuple.getFirst() != null) {
                return false;
            }
            
            if (this.getSecond() != null && !this.getSecond().equals(otherTuple.getSecond())) {
                return false;
            }
            if (this.getSecond() == null && otherTuple.getSecond() != null) {
                return false;
            }
            
            if (this.getThird() != null && !this.getThird().equals(otherTuple.getThird())) {
                return false;
            }
            if (this.getThird() == null && otherTuple.getThird() != null) {
                return false;
            }
            
            return true;
        }
    }
    
    public static class Tuple4<S, T, U, W> implements Serializable {
        private static final long serialVersionUID = 1L;
        private S first;
        private T second;
        private U third;
        private W forth;
        
        public Tuple4(S first, T second, U third, W forth) {
            this.first = first;
            this.second = second;
            this.third = third;
            this.forth = forth;
        }
        
        public S getFirst() {
            return first;
        }
        
        public T getSecond() {
            return second;
        }
        
        public U getThird() {
            return third;
        }
        
        public W getForth() {
            return forth;
        }
        
        @Override
        public int hashCode() {
            int hc = 0;
            if (first != null) {
                hc += 7 * first.hashCode();
            }
            if (second != null) {
                hc += 13 * second.hashCode();
            }
            
            if (third != null) {
                hc += 17 * third.hashCode();
            }
            
            if (forth != null) {
                hc += 23 * forth.hashCode();
            }
            
            return hc;
        }
        
        @Override
        public boolean equals(Object other) {
            if (!(other instanceof Tuple4<?, ?, ?, ?>)) {
                return false;
            }
            
            final Tuple4<?, ?, ?, ?> otherTuple = (Tuple4<?, ?, ?, ?>) other;
            
            if (this.getFirst() != null && !this.getFirst().equals(otherTuple.getFirst())) {
                return false;
            }
            if (this.getFirst() == null && otherTuple.getFirst() != null) {
                return false;
            }
            
            if (this.getSecond() != null && !this.getSecond().equals(otherTuple.getSecond())) {
                return false;
            }
            if (this.getSecond() == null && otherTuple.getSecond() != null) {
                return false;
            }
            
            if (this.getThird() != null && !this.getThird().equals(otherTuple.getThird())) {
                return false;
            }
            if (this.getThird() == null && otherTuple.getThird() != null) {
                return false;
            }
            
            if (this.getForth() != null && !this.getForth().equals(otherTuple.getForth())) {
                return false;
            }
            if (this.getSecond() != null && this.getForth() == null && otherTuple.getForth() != null) {
                return false;
            }
            
            return true;
        }
    }
    
    public static class Tuple5<S, T, U, W, X> implements Serializable {
        private static final long serialVersionUID = 1L;
        private S first;
        private T second;
        private U third;
        private W forth;
        private X fifth;
        
        public Tuple5(S first, T second, U third, W forth, X fifth) {
            this.first = first;
            this.second = second;
            this.third = third;
            this.forth = forth;
            this.fifth = fifth;
        }
        
        public S getFirst() {
            return first;
        }
        
        public T getSecond() {
            return second;
        }
        
        public U getThird() {
            return third;
        }
        
        public W getForth() {
            return forth;
        }
        
        public X getFifth() {
            return fifth;
        }
        
        @Override
        public int hashCode() {
            int hc = 0;
            if (first != null) {
                hc += 7 * first.hashCode();
            }
            if (second != null) {
                hc += 13 * second.hashCode();
            }
            
            if (third != null) {
                hc += 17 * third.hashCode();
            }
            
            if (forth != null) {
                hc += 23 * forth.hashCode();
            }
            
            if (fifth != null) {
                hc += 29 * fifth.hashCode();
            }
            
            return hc;
        }
        
        @Override
        public boolean equals(Object other) {
            if (!(other instanceof Tuple5<?, ?, ?, ?, ?>)) {
                return false;
            }
            
            final Tuple5<?, ?, ?, ?, ?> otherTuple = (Tuple5<?, ?, ?, ?, ?>) other;
            
            if (this.getFirst() != null && !this.getFirst().equals(otherTuple.getFirst())) {
                return false;
            }
            if (this.getFirst() == null && otherTuple.getFirst() != null) {
                return false;
            }
            
            if (this.getSecond() != null && !this.getSecond().equals(otherTuple.getSecond())) {
                return false;
            }
            if (this.getSecond() == null && otherTuple.getSecond() != null) {
                return false;
            }
            
            if (this.getThird() != null && !this.getThird().equals(otherTuple.getThird())) {
                return false;
            }
            if (this.getThird() == null && otherTuple.getThird() != null) {
                return false;
            }
            
            if (this.getForth() != null && !this.getForth().equals(otherTuple.getForth())) {
                return false;
            }
            if (this.getSecond() != null && this.getForth() == null && otherTuple.getForth() != null) {
                return false;
            }
            
            if (this.getFifth() != null && !this.getFifth().equals(otherTuple.getFifth())) {
                return false;
            }
            if (this.getFifth() == null && otherTuple.getFifth() != null) {
                return false;
            }
            
            return true;
        }
    }
}
